﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.IO;
using SubhadraSolutions.Sharp.Utils.Collections.Concurrent;
using SubhadraSolutions.Sharp.Utils.Collections;

namespace SubhadraSolutions.Sharp.Utils.IO
{
    public sealed class AdvancedFileSystemWatcher : AbstractInitializableAndDisposable
    {
        private FileSystemWatcher _watcher;
        private LinkedList<FileInfo> _list = null;
        private object _syncLock = new object();
        public AdvancedFileSystemWatcher(string directoryName,  bool includeExistingFiles = true, string filter = null, bool includeSubdirectories = false)
        {
            IncludeExistingFiles = includeExistingFiles;
            _watcher.IncludeSubdirectories = includeSubdirectories;
            DirectoryName = directoryName;
            Filter = filter;
            IncludeSubdirectories = includeSubdirectories;
            _list = new LinkedList<FileInfo>();
            _watcher = new FileSystemWatcher(directoryName, filter);
            _watcher.EnableRaisingEvents = false;
        }
        public bool IncludeExistingFiles
        {
            get;
            private set;
        }
        public int InternalBufferSize
        {
            get { return _watcher.InternalBufferSize; }
            set { _watcher.InternalBufferSize = value; }
        }
        public string DirectoryName
        {
            get;
            private set;
        }
        public string Filter
        {
            get;
            private set;
        }
        public bool IncludeSubdirectories
        {
            get;
            private set;
        }

        protected override void initializeProtected()
        {
            lock (_syncLock)
            {
                _watcher.Renamed += _watcher_Renamed;
                _watcher.Created += _watcher_Created;
                _watcher.Deleted += _watcher_Deleted;
                _watcher.EnableRaisingEvents = true;
                if (IncludeExistingFiles)
                {
                    var di = new DirectoryInfo(DirectoryName);
                    FileInfo[] files = di.GetFiles(Filter);
                    Array.Sort(files, LastWrittenDateComparer.Instance);
                    foreach (var file in files)
                    {
                        _list.AddLast(file);
                    }
                }
            }
        }

        public FileInfo GetNextFile()
        {
            lock (_syncLock)
            {
                LinkedListNode<FileInfo> firstNode = _list.First;
                if (firstNode == null)
                {
                    return null;
                }
                _list.RemoveFirst();
                return firstNode.Value;
            }
        }
        void _watcher_Deleted(object sender, FileSystemEventArgs e)
        {
            lock(_syncLock)
            {
                deleted(e.FullPath);
            }
        }

        private void deleted(string filePath)
        {
            var fi = new FileInfo(filePath);
            if (!fi.Exists)
            {
                _list.Remove(fi);
            }
        }
        private void created(string filePath)
        {
            var fi = new FileInfo(filePath);
            if (fi.Exists)
            {
                _list.AddLast(fi);
            }
        }
        private void renamed(string oldFilePath, string newFilepath)
        {
            deleted(oldFilePath);
            created(newFilepath);
        }
        void _watcher_Created(object sender, FileSystemEventArgs e)
        {
            lock(_syncLock)
            {
                deleted(e.FullPath);
                created(e.FullPath);
            }
        }

        void _watcher_Renamed(object sender, RenamedEventArgs e)
        {
            lock(_syncLock)
            {
                renamed(e.OldFullPath, e.FullPath);
            }
        }

        protected override void Dispose(bool disposing)
        {
            _watcher.Dispose();
        }
    }
}
